package org.jflame.commons.net.http;

import org.jflame.commons.convert.ConvertException;
import org.jflame.commons.model.CallResult;
import org.jflame.commons.net.HttpHelper;
import org.jflame.commons.net.http.handler.JsonResponseHandler;
import org.jflame.commons.net.http.handler.ResponseBodyHandler;
import org.jflame.commons.net.http.handler.TextResponseHandler;
import org.jflame.commons.net.http.handler.XmlResponseHandler;
import org.jflame.commons.reflect.TypeRef;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;

/**
 * http请求返回结果
 * 
 * @author charles.zhang
 */
public class HttpResponse extends CallResult<byte[]> {

    private static final long serialVersionUID = -8303137663872800766L;
    private Map<String,List<String>> headers;// http headers
    private String charset;
    private String requestURL;

    public HttpResponse() {
    }

    public HttpResponse(int status) {
        setStatus(status);
    }

    public HttpResponse(int status, String message) {
        super(status, message);
    }

    public Map<String,List<String>> getHeaders() {
        return headers;
    }

    public void setHeaders(Map<String,List<String>> headers) {
        this.headers = headers;
    }

    /**
     * 获取指定header的值,如果有多个值只取首个
     * 
     * @param headField
     * @return
     */
    public String getHeader(String headField) {
        if (headers.containsKey(headField)) {
            return headers.get(headField)
                    .get(0);
        }
        return null;
    }

    public List<String> getHeaders(String headField) {
        return headers.get(headField);
    }

    /**
     * 返回cookies,原始字符串格式
     * 
     * @return
     */
    public String getCookiesStr() {
        CookieManager cookieManager = (CookieManager) CookieHandler.getDefault();
        if (cookieManager == null) {
            if (headers.containsKey("Set-Cookie")) {
                return headers.get("Set-Cookie")
                        .get(0);
            } else if (headers.containsKey("Set-Cookie2")) {
                return headers.get("Set-Cookie2")
                        .get(0);
            }
        } else {
            return HttpHelper.getCookies(URI.create(getRequestURL()));
        }

        return null;
    }

    /**
     * 返回cookies
     * 
     * @return
     */
    public List<HttpCookie> getCookies() {
        CookieManager cookieManager = (CookieManager) CookieHandler.getDefault();
        if (cookieManager == null) {
            String cookieStr = getCookiesStr();
            if (cookieStr != null) {
                return HttpCookie.parse(cookieStr);
            }
        } else {
            return cookieManager.getCookieStore()
                    .get(URI.create(getRequestURL()));
        }

        return null;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public String getRequestURL() {
        return requestURL;
    }

    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    public boolean success() {
        return getStatus() == HttpURLConnection.HTTP_OK;
    }

    /**
     * 请求结果作为字符串返回
     * 
     * @return
     */
    public String getResponseAsText() {
        return getResponse(TextResponseHandler.instance);
    }

    /**
     * {@link #getResponseAsText()} 别名
     * 
     * @return
     * @throws IOException
     */
    public String asText() {
        return getResponse(TextResponseHandler.instance);
    }

    /**
     * 请求结果作为JSON字符串反序列化为java对象.<b>只适用于单一对象,List,Map等复杂对象请使用</b>{@link #getResponseAsJson(TypeRef)}}
     * 
     * @param entityClass java对象类型
     * @return
     * @see #getResponseAsJson(TypeRef)
     */
    public <T> T getResponseAsJson(Class<T> entityClass) throws IOException {
        ResponseBodyHandler<T> responseHandler = new JsonResponseHandler<>(entityClass);
        return getResponse(responseHandler);
    }

    /**
     * 请求结果作为JSON字符串反序列化为java对象
     * 
     * @param typeReference 目标对象类型表达TypeRef
     * @return
     */
    public <T> T getResponseAsJson(TypeRef<T> typeReference) throws IOException {
        ResponseBodyHandler<T> responseHandler = new JsonResponseHandler<>(typeReference);
        return getResponse(responseHandler);
    }

    /**
     * 请求结果作为XML反序列化为java对象
     * 
     * @param entityClass
     * @return
     */
    public <T> T getResponseAsXml(Class<T> entityClass) throws IOException {
        return getResponse(new XmlResponseHandler<>(entityClass));
    }

    /**
     * 将返回数据byte[]
     * 
     * @return
     */
    public byte[] getResponseBody() {
        return getData();
    }

    /**
     * 返回请求结果反序列化为CallResult&lt;T&gt;
     * 
     * @param entityClass CallResult data的类型
     * @return
     */
    public <T> CallResult<T> getResponseAsCallResult(Class<T> entityClass) throws IOException {
        return getResponseAsJson(new TypeRef<CallResult<T>>(entityClass) {
        });
    }

    /**
     * {@link #getResponseAsCallResult(Class)}别名
     * 
     * @param <T>
     * @param entityClass
     * @return
     * @throws IOException
     */
    public <T> CallResult<T> asCallResult(Class<T> entityClass) throws IOException {
        return getResponseAsJson(new TypeRef<CallResult<T>>(entityClass) {
        });
    }

    /**
     * 返回请求结果反序列化为CallResult&lt;List&lt;T&gt;&gt;
     * 
     * @param listElementClass CallResult data集合元素的类型
     * @return
     */
    public <T> CallResult<List<T>> getResponseAsCallResultWithList(Class<T> listElementClass) throws IOException {
        return getResponseAsJson(new TypeRef<CallResult<List<T>>>(listElementClass) {
        });
    }

    /**
     * 返回是json格式,转为{@code Map<String,Object>}
     * 
     * @return
     */
    public Map<String,Object> getResponseAsJsonMap() throws IOException {
        return getResponseAsJson(new TypeRef<Map<String,Object>>() {
        });
    }

    /**
     * 返回是json格式,转为{@code Map<String,String>}
     *
     * @return
     * @throws IOException
     */
    public Map<String,String> asStrMap() throws IOException {
        return getResponseAsJson(new TypeRef<Map<String,String>>() {
        });
    }

    /**
     * 同名 {@link #getResponseAsJson(Class)}
     * 
     * @param <T>
     * @param entityClass
     * @return
     * @throws IOException
     */
    public <T> T asJson(Class<T> entityClass) throws IOException {
        ResponseBodyHandler<T> responseHandler = new JsonResponseHandler<>(entityClass);
        return getResponse(responseHandler);
    }

    /**
     * {@link #getResponseAsJson(TypeRef)}别名
     * 
     * @param <T>
     * @param typeReference
     * @return
     * @throws IOException
     */
    /*public <T> T asJson(TypeRef<T> typeReference) throws IOException {
        ResponseBodyHandler<T> responseHandler = new JsonResponseHandler<>(typeReference);
        return getResponse(responseHandler);
    }*/

    /**
     * 返回数据保存为文件. <strong>注:返回结果处理流程是先缓存到内存byte[]再做转换,所以不适合大文件下载</strong>
     * 
     * @param file 要保存的文件路径
     * @return
     * @throws IOException
     */
    public File asFile(Path file) throws IOException {
        return getResponse(new ResponseBodyHandler<File>() {

            @Override
            public File handle(HttpResponse response) throws ConvertException {
                try {
                    Files.copy(new ByteArrayInputStream(getData()), file);
                } catch (IOException e) {
                    throw new ConvertException(e);
                }
                return file.toFile();
            }
        });
    }

    /**
     * 返回结果数据处理. inputStream读取完成后置空,所以不能重复读取
     * 
     * @param handler 数据处理器
     * @return
     */
    public <T> T getResponse(ResponseBodyHandler<T> handler) {
        return handler.handle(this);
    }

    @Override
    public String toString() {
        return "HttpResponse [headers=" + headers + ", charset=" + charset + ", status=" + getStatus() + ", message="
                + getMessage() + "]";
    }

}
